import { EventEmitter } from "stream";
import { MyServer } from "./MyServer";
import { WebSocket } from "ws";
import {
  ApiMsgEnum,
  IModel,
  binaryDecode,
  binaryEncode,
  strdecode,
  strencode,
} from "../Common";
import { buffer2ArrayBuffer } from "../Utils";

// const em = new EventEmitter();
interface IItem {
  cb: Function;
  ctx: unknown;
}

export class Connection extends EventEmitter {
  private msgMap: Map<ApiMsgEnum, Array<IItem>> = new Map();

  constructor(private server: MyServer, private ws: WebSocket) {
    super();
    this.ws.on("close", () => {
      //   this.server.connections.delete(this);
      this.emit("close");
    });

    this.ws.on("message", (buffer: Buffer) => {
      this.handleMsg(buffer);
    });
  }

  handleMsg(buffer: Buffer) {
    try {
      // console.log("msg: " + buffer.toString());
      // const ta = new Uint8Array(buffer);
      // const str = strdecode(ta);

      // const json = JSON.parse(str);

      const json = binaryDecode(buffer2ArrayBuffer(buffer));

      const { name, data } = json;
      // console.log("data: " + data.toString());

      if (this.server.apiMap.has(name)) {
        try {
          const cb = this.server.apiMap.get(name);
          const res = cb.call(null, this, data);
          this.sendMsg(name, {
            success: true,
            res,
          });
        } catch (e) {
          this.sendMsg(name, {
            success: false,
            error: e.message,
          });
        }

        return;
      }

      try {
        if (this.msgMap.has(name)) {
          this.msgMap.get(name).forEach(({ cb, ctx }) => {
            cb.call(ctx, this, data);
          });
        }
      } catch (e) {
        console.log(e);
      }
    } catch (e) {
      console.log(e);
    }
  }

  sendMsg<T extends keyof IModel["msg"]>(name: T, data: IModel["msg"][T]) {
    const msg = { name, data };

    // const str = JSON.stringify(msg);
    // const ta = strencode(str);
    // const buff = Buffer.from(ta);

    const bc = binaryEncode(name, data);
    const buff = Buffer.from(bc.buffer);

    this.ws.send(buff);
  }

  listenMsg<T extends keyof IModel["msg"]>(
    name: T,
    cb: (connection: Connection, args: IModel["msg"][T]) => void,
    ctx: unknown
  ) {
    if (this.msgMap.has(name)) {
      this.msgMap.get(name).push({ cb, ctx });
    } else {
      this.msgMap.set(name, [{ cb, ctx }]);
    }
  }

  unlistenMsg<T extends keyof IModel["msg"]>(
    name: T,
    cb: (connection: Connection, args: IModel["msg"][T]) => void,
    ctx: unknown
  ) {
    if (this.msgMap.has(name)) {
      const index = this.msgMap
        .get(name)
        .findIndex((i) => cb === i.cb && i.ctx === ctx);
      index > -1 && this.msgMap.get(name).splice(index, 1);
    }
  }
}
